package com.utilities.parsers;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

import com.utilities.IO.FileUtilities;
import com.utilities.dataStructures.Range;

public class PrimeFinder {
	
	public static void main(String[] args) throws Exception
	{
		String inputcontigsfile = "C:/Users/bansalp/home/Desktop/UZH/newCosmid/run1run2/all/contigs.fna";
		String inputSegmentsfile = "C:/Users/bansalp/home/Desktop/UZH/newCosmid/run1run2/all/finalSegments.txt";
		String outputLeftFile = "C:/Users/bansalp/home/Desktop/UZH/newCosmid/run1run2/all/left_primers.fna";
		String outputrightFile = "C:/Users/bansalp/home/Desktop/UZH/newCosmid/run1run2/all/right_primers.fna";
		String out = "C:/Users/bansalp/home/Desktop/UZH/newCosmid/run1run2/all/primers.fna";
		PrimeFinder finder = new PrimeFinder();
//		ArrayList<Range> lst = finder.findRightPrimers(finder.readSegments(inputSegmentsfile), inputcontigsfile);
//		FileUtilities.WriteToFile(lst, outputrightFile);
//		lst = finder.findLeftPrimers(finder.readSegments(inputSegmentsfile), inputcontigsfile);
//		FileUtilities.WriteToFile(lst, outputLeftFile);
//		finder.write(outputrightFile, outputLeftFile, out);
		finder.find(inputcontigsfile,inputSegmentsfile);
//		finder.sortMe("C:/Users/bansalp/home/Desktop/UZH/newCosmid/run1run2/all/primers_result.fna", 
//				"C:/Users/bansalp/home/Desktop/UZH/newCosmid/run1run2/all/primers_sort.fna");
	}
	
	public void write(String right, String left, String out)
	{
		ArrayList<String> lst = new ArrayList<String>();
		try
		{
			BufferedReader buff1_left = new BufferedReader(new FileReader(left));
			BufferedReader buff2_right = new BufferedReader(new FileReader(right));
			String str1 = buff1_left.readLine();
			String str2 = buff2_right.readLine();
			while(str1 != null || str2 != null)
			{
				String[] params1 = str1.split("\t");
				String[] params2 = str2.split("\t");
				
				StringBuffer strBuff = new StringBuffer();
				strBuff.append(String.format(">%s_UP %s..%s length=%s\n%s\n",params1[2], params1[0], params1[1],params1[3],params1[4]));
				strBuff.append(String.format(">%s_DOWN %s..%s length=%s\n%s",params2[2], params2[0], params2[1],params2[3],params2[4]));
				lst.add(strBuff.toString());
				str1 = buff1_left.readLine();
				str2 = buff2_right.readLine();
			}
			FileUtilities.writeToFile(lst, out);
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}
	
	public void sortMe(String filein, String fileout) throws Exception
	{
		ArrayList<String> lst = new ArrayList<String>();
		BufferedReader buff1_left = new BufferedReader(new FileReader(filein));
		String str1 = buff1_left.readLine();
		while(str1 != null)
		{
			System.out.println(str1);
			str1 = str1.trim();
			lst.add(str1+ "~" +  buff1_left.readLine().trim());
			str1 = buff1_left.readLine();
		}
		buff1_left.close();
		
		Collections.sort(lst, new Comparator<String>(){

			@Override
			public int compare(String arg0, String arg1) {
				if (arg0.contains("UP") && arg1.contains("DOWN"))
				{
					return 0;
				}
				else if (arg1.contains("UP") && arg0.contains("DOWN"))
				{
					return 1;
				}
				else 
				{
					String params0 = arg0.split("~")[0].split(" ")[3].split("=")[1];
					String params1 = arg1.split("~")[0].split(" ")[3].split("=")[1];
					int start0 = Integer.parseInt(params0);
					int start1 = Integer.parseInt(params1);
					return start0-start1;
				}
			}
		});
		PrintWriter printWriter = new PrintWriter(fileout);
		for(String primer : lst)
		{
			String[] params = primer.split("~");
			printWriter.println(String.format("%s\n%s",params[0],params[1]));
		}
		printWriter.flush();
		printWriter.close();
	}

	public ArrayList<Range> find(String inputcontigsfile, String outputSegmentFile) throws Exception
	{
//		int segmentLength = 1560;
//		int seqLength = 66000;
//		int minimumSegMentLength = 800;
//		int extension = 200;
//		int overlap = 160;
		int segmentLength = 10000;
		int seqLength = 66669;
		int minimumSegMentLength = 800;
		int extension = 200;
		int overlap = 200;
		ArrayList<Range> contigs = readFromFile(inputcontigsfile);
		ArrayList<Range> result = new ArrayList<Range>();
		boolean flag = false;
		Range startContig = findStartContig(contigs, 0,overlap);
		int start = startContig.getLowerLimit();
		int end = start + segmentLength;
		while(end <= seqLength)
		{
			Range endContig = findEndContig(contigs, end,overlap);
			if (endContig == null)
			{
				
				int newend = end + extension;
				endContig = findEndContig(contigs, newend,overlap);
				if (endContig != null)
				{
					end = newend;
				}
				else
				{
					for(int i = newend; i > end - minimumSegMentLength; )
					{
						endContig = findEndContig(contigs, i,overlap);
						if (endContig != null)
						{
							end = i;
							break;
						}
						i = i - 20;
					}
				}
				if (endContig == null)
				{
					endContig = findNearestLowest(contigs, newend, start,overlap);
					Range r = result.get(result.size() - 1);
					for(Range rng : result)
					{
						if (rng.getUpperLimit() == endContig.getLowerLimit() + (int)endContig.getActualLength() - 1)
						{
							endContig = null;
							break;
						}
					}
//					if (endContig.getLowerLimit() + (int)endContig.getActualLength() <= r.getUpperLimit())
//						endContig = null;
					if (endContig != null)
					{
						end = endContig.getLowerLimit() + (int)endContig.getActualLength() - 1;
					}
					else
					{
						flag = true;
					}
					
				}
				
//				int newend = end + 200;
//				endContig = findEndContig(contigs, newend);
//				if (endContig != null)
//				{
//					end = newend;
//				}
//				else
//				{
//					endContig = findNearestLowest(contigs, newend);
//					flag = true;
//				}
			}
			if (endContig != null)
			{
				if (!flag)
				{
					Range r = new Range(start, end, startContig.getMetadata() + ":" + endContig.getMetadata());
					add(result, r);
//					System.out.println(r.getLowerLimit() + " " + r.getUpperLimit()) ;
					start = end - overlap;
					end = start + segmentLength;
					startContig = endContig;
				}
				else
				{
					Range r = new Range(start, end, startContig.getMetadata() + ":" + endContig.getMetadata());
					add(result, r);
//					System.out.println(r.getLowerLimit() + " " + r.getUpperLimit()) ;
					startContig = findStartContig(contigs,end + 1,overlap); 
					if (startContig == null)
					{
						break;
					}
					start = startContig.getLowerLimit();
					end = start + segmentLength;
					flag = false;
//					start = end - 70;
//					end = start + 1600;
//					startContig = endContig;
				}
			}
			else
			{
//				endContig = findNearestLowest(contigs,end);
				Range r = new Range(start, startContig.getUpperLimit(), startContig.getMetadata() + ":" 
						+ startContig.getMetadata());
				add(result,r);
//				System.out.println(r.getLowerLimit() + " " + r.getUpperLimit()) ;
				startContig = findStartContig(contigs,end,overlap); 
				if (startContig == null)
				{
					break;
				}
				start = startContig.getLowerLimit();
				end = start + segmentLength; 
			}
		}
		FileUtilities.WriteToFile(result, outputSegmentFile);
		return result;
	}

	private void add(ArrayList<Range> result, Range r) {
		System.out.println(r.getLowerLimit() + " " + r.getUpperLimit()) ;
		result.add(r);
	}

	private Range findNearestLowest(ArrayList<Range> contigs, int end, int start, int overlap) 
	{
		System.out.println(start + " " + end);
		Range rangePassed = null;
		Range previousRange = null;
		int i = 0;
		for(; i < contigs.size();i++)
		{
			Range range = contigs.get(i);
			if (i == 0)
			{
				previousRange = range;
			}
			else
			{
				if (end > previousRange.getUpperLimit() && end < range.getLowerLimit())
				{
					rangePassed = previousRange;
					break;
				}
				else if (end > range.getLowerLimit() && end < range.getUpperLimit())
				{
					rangePassed = range;
					break;
				}
				else
				{
					previousRange = range;
				}
			}
		}
		if (rangePassed != null)
		{
			if (rangePassed.getActualLength() > overlap)
			{
				if (start > rangePassed.getLowerLimit() && start < (rangePassed.getLowerLimit() + rangePassed.getActualLength() - 1)
						&& (rangePassed.getLowerLimit() + rangePassed.getActualLength() - 1 - start) >= overlap)
				{
					return rangePassed;
				}
				else if (end > rangePassed.getLowerLimit() && end < (rangePassed.getLowerLimit() + rangePassed.getActualLength() - 1)
						&& end - rangePassed.getLowerLimit() >= overlap)
				{
					return rangePassed;
				}
				else
				{
					return findNearestLowest(contigs,end-20,start,overlap);
				}
			}
			return findNearestLowest(contigs,end-20,start,overlap);
		}
		else
		{
			while (i >= 0)
			{
				i--;
				Range range = contigs.get(i);
				if (range.getActualLength() > overlap && (range.getLowerLimit() + range.getActualLength() - 1 - start) >= overlap)
				{
					return range;
				}
			}
		}
		
		return null;
	}

	private Range findStartContig(ArrayList<Range> contigs, int end, int overlap) 
	{
		for(Range contig : contigs)
		{
			if (end < contig.getLowerLimit() && contig.getActualLength() >=overlap)
			{
				return contig;
			}
		}
		return null;
	}

	private Range findEndContig(ArrayList<Range> contigs, int end, int overlap) {
		Range contig = null;
		for(Range _contig : contigs)
		{
			if (_contig.getLowerLimit() < end && end < (_contig.getLowerLimit() + _contig.getActualLength() - 1))
			{
				if (end - _contig.getLowerLimit() >= overlap)
				{
					// read the contig string from the file and find if it ends with G/C
					contig = _contig;
					break;
				}
			}
			else if (end < _contig.getLowerLimit())
			{
				break;
			}
		}
		return contig;
	}

	public ArrayList<Range> readFromFile(String file) throws Exception
	{
		ArrayList<Range> arrayList = new ArrayList<Range>();
		BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
		String readLine = bufferedReader.readLine();
		while(readLine != null)
		{
			if (readLine.startsWith(">"))
			{
				String[] params = readLine.trim().replaceAll("( )+", " ").split(" ");
				int start = Integer.parseInt(params[2].split("[.][.]")[0]);
				int end = Integer.parseInt(params[2].split("[.][.]")[1]);
				int length = Integer.parseInt(params[3].split("=")[1]);
				String name = params[0].substring(1);
				Range range = new Range(start,end,name);
				range.setActualLength((double)length);
				arrayList.add(range);
			}
			readLine = bufferedReader.readLine();
		}
		return arrayList;
	}
	
	private ArrayList<Range> findRightPrimers(ArrayList<Range> segments, String contigsFile) throws Exception
	{
		ArrayList<Range> arrayList = new ArrayList<Range>();
		for(Range segment : segments)
		{
			String endContig = segment.getMetadata().split(":")[1];
			Range r = readContigFromFile(endContig,contigsFile);
			int diff = segment.getUpperLimit() - r.getLowerLimit() - 19;
			char c = r.getMetadata().charAt(diff);
			if (!(c == 'c' || c== 'C' || c == 'g' || c == 'G'))
			{
//				diff = diff + 18;
				while(diff >= 0)
				{
					diff--;
					c = r.getMetadata().charAt(diff);
					if (c == 'c' || c== 'C' || c == 'g' || c == 'G') 
						break;
				}
			}
			if (diff < 0 ) 
			{
				System.out.println(segment.getLowerLimit() + "\t" + segment.getUpperLimit());
				System.exit(1);
			}
			else
			{
				String str = r.getMetadata().substring(diff, segment.getUpperLimit() - r.getLowerLimit() + 1);
				System.out.println(segment.getLowerLimit() + " " + segment.getUpperLimit());
				Range rg = new Range(segment.getLowerLimit(), segment.getUpperLimit(), segment.getMetadata() + "\t" + 
						str.length() + "\t" + str + "\t" + reverseComplement(str));
				arrayList.add(rg);
			}
		}
		return arrayList;
	}
	
	private ArrayList<Range> findLeftPrimers(ArrayList<Range> segments, String contigsFile) throws IOException
	{
		ArrayList<Range> arrayList = new ArrayList<Range>();
		try
		{
			for(Range segment : segments)
			{
				String startContig = segment.getMetadata().split(":")[0];
				Range r = readContigFromFile(startContig,contigsFile);
				int diff = segment.getLowerLimit() - r.getLowerLimit() + 19;
				char c = r.getMetadata().charAt(diff);
				if (!(c == 'c' || c== 'C' || c == 'g' || c == 'G'))
				{
//					diff = diff + 18;
					while(diff < r.getMetadata().length())
					{
						diff++;
						c = r.getMetadata().charAt(diff);
						if (c == 'c' || c== 'C' || c == 'g' || c == 'G') 
							break;
					}
				}
				if (diff <= 0 ) 
				{
					System.out.println(segment.getLowerLimit() + "\t" + segment.getUpperLimit());
					System.exit(1);
				}
				else
				{
					String str = r.getMetadata().substring(segment.getLowerLimit() - r.getLowerLimit(), diff + 1);
					System.out.println(segment.getLowerLimit() + " " + segment.getUpperLimit());
					Range rg = new Range(segment.getLowerLimit(), segment.getUpperLimit(), segment.getMetadata() + "\t" + 
							str.length() + "\t" + str + "\t" + str);
					arrayList.add(rg);
				}
			}
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
		return arrayList;
	}
	
	private String reverseComplement(String str)
	{
		StringBuffer strBuff = new StringBuffer();
		str = str.toUpperCase();
		for (int i = str.length() - 1 ; i >= 0;i--)
        {
        	char ch = str.charAt(i);
        	switch(ch)
    		{
    		case 'A' : ch = 'T';
    					break;
    		case 'a' : ch = 't';
    					break;
    		case 'C' : ch = 'G';
    					break;
    		case 'c' : ch = 'g';
    				    break;
    		case 'T' : ch = 'A';
    					break;
    		case 't' : ch = 'a';
    					break;
    		case 'G' : ch = 'C';
    					break;
    		case 'g' : ch = 'c';
    					break;
    		}
        	strBuff.append(ch);
        }
		return strBuff.toString();
	}
	
	private String complement(String str)
	{
		StringBuffer strBuff = new StringBuffer();
		str = str.toUpperCase();
		for (int i = 0 ; i < str.length();i++)
        {
        	char ch = str.charAt(i);
        	switch(ch)
    		{
    		case 'A' : ch = 'T';
    					break;
    		case 'a' : ch = 't';
    					break;
    		case 'C' : ch = 'G';
    					break;
    		case 'c' : ch = 'g';
    				    break;
    		case 'T' : ch = 'A';
    					break;
    		case 't' : ch = 'a';
    					break;
    		case 'G' : ch = 'C';
    					break;
    		case 'g' : ch = 'c';
    					break;
    		}
        	strBuff.append(ch);
        }
		return strBuff.toString();
	}
	
	private Range readContigFromFile(String startContig, String file) throws IOException {
		BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
		String readLine = bufferedReader.readLine();
		boolean flag = false;
		Range r = null;
		StringBuffer strBuff = new StringBuffer();
		while(readLine != null)
		{
			if (flag && readLine.startsWith(">"))
			{
				break;
			}
			else if (readLine.contains(startContig) || flag)
			{
				if (r==null)
				{
					String[] params = readLine.trim().replaceAll("( )+", " ").split(" ");
					int start = Integer.parseInt(params[2].split("[.][.]")[0]);
					int end = Integer.parseInt(params[2].split("[.][.]")[1]);
					int length = Integer.parseInt(params[3].split("=")[1]);
					String name = params[0].substring(1);
					r = new Range(start,end,name);
					r.setActualLength((double)length);
					flag = true;
				}
				else
				{
					strBuff.append(readLine.trim());
				}
			}
			readLine = bufferedReader.readLine();
		}
		r = new Range(r.getLowerLimit(), r.getUpperLimit(), strBuff.toString());
		return r;
	}

	private ArrayList<Range> readSegments(String file) throws Exception 
	{
		ArrayList<Range> arrayList = new ArrayList<Range>();
		BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
		String readLine = bufferedReader.readLine();
		while(readLine != null)
		{
			String[] params = readLine.split("\t");
//			System.out.println(readLine);
			int start = Integer.parseInt(params[0]);
			int end = Integer.parseInt(params[1]);
			String metadata = params[2];
			Range r = new Range(start, end, metadata);
			arrayList.add(r);
			readLine = bufferedReader.readLine();
		}
		return arrayList;
	}
}
